package cn.tedu.charging.order.points;

import com.alibaba.fastjson2.JSON;
import jakarta.websocket.*;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 服务器中websocket的一个对外暴露的连接端点
 * 在代码中关心以下内容
 * 1.然客户端请求连接哪里(地址路径)
 * 2.连接能否成功建立(类似mqttClient connectComplete)
 * 3.连接何时断开 因为什么断开(类似mqttClient connectionLost)
 * 4.连接建立之后,客户端发送信息接收(类似mqttClient messageArrived)
 * 5.消息通信过程各种异常的管理
 */
@Component
@Slf4j
@ServerEndpoint("/ws/server/{userId}")//http://localhost:8180/ws/server
public class WebsocketServerPoint<T> {
    private static final Map<Integer,Session> SESSIONS=new ConcurrentHashMap<>();
    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session,@PathParam("userId") Integer userId){
        log.info("有客户端建立连接,连接的sessionId:{},客户userId:{}",session.getId(),userId);
        //1.利用userId 查询未读消息个数
        //2.使用session 将数字 信息返回客户端

        try{
            //使用session,在连接建立方法onOpen调用的时候,谁连的,给谁发送打招呼信息
            String msg="你好,我是服务端,欢迎连接";
            session.getBasicRemote().sendText(msg);
        }catch (Exception e){
            log.error("打招呼发送消息失败",e);
        }
        //将建立好的session连接,与客户端传递过来的业务数据userId 在map中做映射存储
        //TODO 如果是同一个用户多次连接
        //1.在key(userId) 后面跟session list
        //2.先判断是否已经链接,如果已连,要不就剔除断开之前连接,要不就不允许新链接
        SESSIONS.put(userId,session);
        log.info("当前系统在线人数:{},在线用户id有:{}",SESSIONS.size(),SESSIONS.keySet());
    }
    /**
     * 断开连接调用的方法
     */
    @OnClose
    public void onClose(Session session,@PathParam("userId") Integer userId){
        log.info("有客户端断开连接,断开的sessionId:{},客户userId:{}",session.getId(),userId);
        //断开连接 session失效了,应该从map里将其移除
        SESSIONS.remove(userId);
        log.info("当前系统在线人数:{}",SESSIONS.size());
    }
    /**
     * 服务端接收到客户端发送的消息信息
     */
    @OnMessage
    public void onMessage(String message,Session session,@PathParam("userId") Integer userId){
        log.info("服务端接收到客户端发送的消息:{},连接会话sessionId:{},客户userId:{}",message,session.getId(),userId);
        try{
            //使用session,每次客户端给我们发送信息的时候,告诉客户端"收到"
            String msg="好的,收到";
            session.getBasicRemote().sendText(msg);
        }catch (Exception e){
            log.error("回复客户端消息失败",e);
        }
    }
    /**
     * 发生错误时调用的方法
     */
    @OnError
    public void onError(Throwable error,Session session,@PathParam("userId") Integer userId){
        log.info("服务端发生异常:{},连接会话sessionId:{},客户userId:{}",error,session.getId(),userId);
    }

    public boolean pushMsg(Integer userId, String msg) {
        //1.准备一个返回值,初始化是false,只要推成功就改成true
        boolean result=false;
        //2.从map中利用映射keyuserId 拿到session
        Session session = SESSIONS.get(userId);
        //判断是否存在
        if (session!=null){
            try{
                //3.推送消息
                session.getBasicRemote().sendText(msg);
                result=true;
            }catch (Exception e){
                log.error("推送消息失败",e);
            }
        }else{
            //TODO 4 推送失败,做消息后续处理
            log.info("推送失败,用户不在线");
        }
        return result;
    }
    //推送不同业务对象的消息给前端解析使用
    public boolean pushMsg(Integer userId, T t){
        //想办法将 msg对象抓话成json字符串
        String msg= JSON.toJSONString(t);
        //1.准备一个返回值,初始化是false,只要推成功就改成true
        boolean result=false;
        //2.从map中利用映射keyuserId 拿到session
        Session session = SESSIONS.get(userId);
        //判断是否存在
        if (session!=null){
            try{
                //3.推送消息
                session.getBasicRemote().sendText(msg);
                result=true;
            }catch (Exception e){
                log.error("推送消息失败",e);
            }
        }else{
            //TODO 4 推送失败,做消息后续处理
            log.info("推送失败,用户不在线");
        }
        return result;
    }
}
